import asyncio
import json
import logging
import time
from typing import Callable, Dict, Any, Optional
from datetime import datetime
import paho.mqtt.client as mqtt
from app.core.config.settings import settings

logger = logging.getLogger(__name__)


class MQTTManager:
    """MQTT管理器"""
    
    def __init__(self):
        self.client: Optional[mqtt.Client] = None
        self.message_handlers: Dict[str, Callable] = {}
        self.is_connected = False
        self.reconnect_timer = None
        self.loop = None
        
    async def connect(self):
        """连接MQTT Broker"""
        try:
            # 创建客户端实例
            client_id = f"meishen_server_{settings.SERVER_ID}_{int(time.time())}"
            self.client = mqtt.Client(client_id=client_id)
            
            # 设置回调函数
            self.client.on_connect = self._on_connect
            self.client.on_disconnect = self._on_disconnect
            self.client.on_message = self._on_message
            self.client.on_subscribe = self._on_subscribe
            self.client.on_unsubscribe = self._on_unsubscribe
            self.client.on_log = self._on_log
            
            # 设置认证
            if settings.MQTT_USERNAME and settings.MQTT_PASSWORD:
                self.client.username_pw_set(
                    settings.MQTT_USERNAME, 
                    settings.MQTT_PASSWORD
                )
            
            # 设置TLS（如果需要）
            if settings.MQTT_USE_TLS:
                self.client.tls_set()
            
            # 连接到MQTT Broker
            self.client.connect_async(
                host=settings.MQTT_HOST,
                port=settings.MQTT_PORT,
                keepalive=settings.MQTT_KEEPALIVE
            )
            
            # 启动网络循环
            self.client.loop_start()
            
            logger.info(f"🔌 正在连接MQTT Broker: {settings.MQTT_HOST}:{settings.MQTT_PORT}")
            
        except Exception as e:
            logger.error(f"❌ MQTT连接失败: {e}")
            raise
    
    def _on_connect(self, client, userdata, flags, rc):
        """连接成功回调"""
        if rc == 0:
            self.is_connected = True
            logger.info(f"✅ MQTT连接建立成功 (Client ID: {client._client_id.decode()})")
            
            # 订阅所有设备主题
            topics = [
                ("meishen/+/+/status/up", 1),           # 设备状态
                ("meishen/+/+/sensor/up", 1),           # 传感器数据
                ("meishen/+/+/alert/up", 1),            # 设备告警
                ("meishen/+/+/emergency/up", 2),        # 紧急情况（QoS 2）
                ("meishen/+/+/heartbeat/up", 0),        # 心跳（QoS 0）
                ("meishen/+/+/location/up", 1),         # 位置信息
                ("meishen/+/+/usage/up", 1),            # 使用统计
            ]
            
            for topic, qos in topics:
                result, mid = client.subscribe(topic, qos)
                if result == mqtt.MQTT_ERR_SUCCESS:
                    logger.info(f"📡 订阅主题: {topic} (QoS: {qos})")
                else:
                    logger.error(f"❌ 订阅主题失败: {topic}, 错误码: {result}")
        else:
            self.is_connected = False
            error_messages = {
                1: "协议版本不正确",
                2: "无效的客户端标识符", 
                3: "服务器不可用",
                4: "错误的用户名或密码",
                5: "未授权"
            }
            error_msg = error_messages.get(rc, f"未知错误: {rc}")
            logger.error(f"❌ MQTT连接失败: {error_msg}")
    
    def _on_disconnect(self, client, userdata, rc):
        """断连回调"""
        self.is_connected = False
        if rc != 0:
            logger.warning(f"⚠️  MQTT意外断开连接，返回码: {rc}")
            # 设置重连
            self._schedule_reconnect()
        else:
            logger.info("📴 MQTT正常断开连接")
    
    def _on_message(self, client, userdata, msg):
        """消息接收回调"""
        try:
            topic = msg.topic
            payload_str = msg.payload.decode('utf-8')
            
            logger.debug(f"📨 收到MQTT消息 - 主题: {topic}, 大小: {len(payload_str)} bytes")
            
            # 解析JSON
            try:
                payload = json.loads(payload_str)
            except json.JSONDecodeError as e:
                logger.error(f"❌ JSON解析失败: {e}, 原始数据: {payload_str}")
                return
            
            # 解析主题
            topic_parts = topic.split('/')
            if len(topic_parts) >= 5:
                location = topic_parts[1]
                device_id = topic_parts[2]
                data_type = topic_parts[3]
                direction = topic_parts[4]
                
                # 验证方向
                if direction != 'up':
                    logger.warning(f"⚠️  忽略非上报消息: {topic}")
                    return
                
                # 添加消息元数据
                message_data = {
                    'topic': topic,
                    'location': location,
                    'device_id': device_id,
                    'data_type': data_type,
                    'payload': payload,
                    'timestamp': datetime.utcnow().isoformat() + 'Z',
                    'qos': msg.qos,
                    'retain': msg.retain
                }
                
                # 调用对应的处理器
                handler_key = f"{data_type}_handler"
                if handler_key in self.message_handlers:
                    # 异步调用处理器
                    if asyncio.get_event_loop().is_running():
                        asyncio.create_task(
                            self.message_handlers[handler_key](message_data)
                        )
                    else:
                        logger.warning(f"⚠️  事件循环未运行，无法处理消息: {topic}")
                else:
                    logger.warning(f"⚠️  未找到处理器: {handler_key}, 主题: {topic}")
            else:
                logger.warning(f"⚠️  主题格式错误: {topic}")
                
        except Exception as e:
            logger.error(f"❌ 处理MQTT消息失败: {e}, 主题: {msg.topic}")
    
    def _on_subscribe(self, client, userdata, mid, granted_qos):
        """订阅成功回调"""
        logger.debug(f"📡 订阅确认 - MID: {mid}, QoS: {granted_qos}")
    
    def _on_unsubscribe(self, client, userdata, mid):
        """取消订阅回调"""
        logger.debug(f"📡 取消订阅确认 - MID: {mid}")
    
    def _on_log(self, client, userdata, level, buf):
        """日志回调"""
        if level == mqtt.MQTT_LOG_ERR:
            logger.error(f"MQTT错误: {buf}")
        elif level == mqtt.MQTT_LOG_WARNING:
            logger.warning(f"MQTT警告: {buf}")
        else:
            logger.debug(f"MQTT日志: {buf}")
    
    def _schedule_reconnect(self, delay: int = 5):
        """安排重连"""
        if self.reconnect_timer:
            return
        
        logger.info(f"⏰ {delay}秒后尝试重连MQTT...")
        self.reconnect_timer = asyncio.get_event_loop().call_later(
            delay, self._attempt_reconnect
        )
    
    def _attempt_reconnect(self):
        """尝试重连"""
        self.reconnect_timer = None
        if not self.is_connected and self.client:
            try:
                self.client.reconnect()
                logger.info("🔄 尝试重连MQTT...")
            except Exception as e:
                logger.error(f"❌ MQTT重连失败: {e}")
                # 增加延迟后再次尝试
                self._schedule_reconnect(delay=10)
    
    def register_handler(self, message_type: str, handler: Callable):
        """注册消息处理器
        
        Args:
            message_type: 消息类型 (status, sensor, alert, emergency, heartbeat, location, usage)
            handler: 处理函数，接受message_data参数
        """
        handler_key = f"{message_type}_handler"
        self.message_handlers[handler_key] = handler
        logger.info(f"📝 注册消息处理器: {handler_key}")
    
    def unregister_handler(self, message_type: str):
        """注销消息处理器"""
        handler_key = f"{message_type}_handler"
        if handler_key in self.message_handlers:
            del self.message_handlers[handler_key]
            logger.info(f"🗑️  注销消息处理器: {handler_key}")
    
    async def publish(self, topic: str, payload: Dict[str, Any], qos: int = 1, retain: bool = False) -> bool:
        """发布消息
        
        Args:
            topic: MQTT主题
            payload: 消息载荷
            qos: 服务质量等级 (0, 1, 2)
            retain: 是否保留消息
            
        Returns:
            bool: 发布是否成功
        """
        if not self.is_connected or not self.client:
            logger.error("❌ MQTT未连接，无法发布消息")
            return False
        
        try:
            # 添加时间戳
            if 'timestamp' not in payload:
                payload['timestamp'] = datetime.utcnow().isoformat() + 'Z'
            
            # 序列化为JSON
            message = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
            
            # 发布消息
            result = self.client.publish(topic, message, qos, retain)
            
            if result.rc == mqtt.MQTT_ERR_SUCCESS:
                logger.info(f"📤 消息发布成功 - 主题: {topic}, QoS: {qos}")
                return True
            else:
                logger.error(f"❌ 消息发布失败 - 主题: {topic}, 错误码: {result.rc}")
                return False
                
        except Exception as e:
            logger.error(f"❌ 发布消息异常: {e}, 主题: {topic}")
            return False
    
    async def publish_device_command(self, location: str, device_id: str, commands: list, operator: str = "system") -> bool:
        """发布设备控制命令
        
        Args:
            location: 位置代码
            device_id: 设备ID
            commands: 命令列表
            operator: 操作者
            
        Returns:
            bool: 发布是否成功
        """
        topic = f"meishen/{location}/{device_id}/control/down"
        
        payload = {
            "timestamp": datetime.utcnow().isoformat() + "Z",
            "device_id": device_id,
            "command_id": f"cmd_{int(time.time() * 1000)}",
            "operator": operator,
            "commands": commands
        }
        
        return await self.publish(topic, payload, qos=1)
    
    async def publish_maintenance_command(self, location: str, device_id: str, maintenance_type: str, params: dict, operator: str) -> bool:
        """发布维护命令
        
        Args:
            location: 位置代码
            device_id: 设备ID
            maintenance_type: 维护类型
            params: 维护参数
            operator: 操作者
            
        Returns:
            bool: 发布是否成功
        """
        topic = f"meishen/{location}/{device_id}/maintenance/down"
        
        payload = {
            "timestamp": datetime.utcnow().isoformat() + "Z",
            "device_id": device_id,
            "command_id": f"mnt_{int(time.time() * 1000)}",
            "operator": operator,
            "maintenance_type": maintenance_type,
            "params": params
        }
        
        return await self.publish(topic, payload, qos=1)
    
    def get_connection_status(self) -> Dict[str, Any]:
        """获取连接状态"""
        return {
            "connected": self.is_connected,
            "client_id": self.client._client_id.decode() if self.client else None,
            "broker_host": settings.MQTT_HOST,
            "broker_port": settings.MQTT_PORT,
            "handlers_count": len(self.message_handlers),
            "registered_handlers": list(self.message_handlers.keys())
        }
    
    async def disconnect(self):
        """断开连接"""
        if self.client:
            try:
                self.client.loop_stop()
                self.client.disconnect()
                logger.info("📴 MQTT连接已断开")
            except Exception as e:
                logger.error(f"❌ 断开MQTT连接失败: {e}")
        
        # 取消重连计时器
        if self.reconnect_timer:
            self.reconnect_timer.cancel()
            self.reconnect_timer = None
        
        self.is_connected = False
        self.client = None


# 全局MQTT管理器实例
mqtt_manager = MQTTManager()